﻿using System;
using System.IO;
using System.Text;

namespace Ukagaka.Named.Ghost.Shiori
{
	/// <summary>
	/// メッセージを表現します
	/// </summary>
	public abstract class Message : IMessage
	{
		/// <summary>
		/// ヘッダ群を取得します
		/// </summary>
		public HeaderList HeaderList
		{
			get;
			private set;
		}

		/// <summary>
		/// メッセージを初期化します
		/// </summary>
		protected Message( )
		{
			Protocol = new Protocol( "SHIORI", 3.0M );
			HeaderList = new HeaderList( );
		}

		/// <summary>
		/// メッセージを取得します
		/// </summary>
		/// <returns>メッセージ</returns>
		public override string ToString( )
		{
			return string.Concat( HeaderList, "\r\n" );
		}

		/// <summary>
		/// プロトコルを取得または設定します
		/// </summary>
		public Protocol Protocol
		{
			get;
			set;
		}

		/// <summary>
		/// 文字エンコーディングを取得または設定します
		/// </summary>
		public Encoding Encoding
		{
			get
			{
				var header = HeaderList.GetHeader( "Charset" );

				if ( null == header )
				{
					return Encoding.ASCII;
				}
				else
				{
					return Encoding.GetEncoding( header.Value );
				}
			}
			set
			{
				HeaderList[ "Charset" ] = null == value ? null : value.WebName;
			}
		}

		/// <summary>
		/// メッセージを取得します
		/// </summary>
		/// <param name="message">メッセージ</param>
		public virtual void Parse( string message )
		{
			HeaderList.Clear( );

			using ( var sr = new StringReader( message ) )
			{
				var line = ( string )null;

				while ( null != ( line = sr.ReadLine( ) ) )
				{
					if ( -1 != line.IndexOf( ':' ) )
					{
						HeaderList.Add( new Header( line ) );
					}
				}
			}

			// 文字エンコーディング情報を更新する
			Encoding = Encoding;
		}

		/// <summary>
		/// メッセージをリクエストまたはレスポンスで取得します
		/// </summary>
		/// <param name="message">メッセージ</param>
		/// <returns>リクエストまたはレスポンス</returns>
		public static Message FromBytes( byte[ ] message )
		{
			var ascii = Encoding.ASCII.GetString( message );
			var idx = ascii.IndexOf( "Charset", StringComparison.InvariantCultureIgnoreCase );

			if ( -1 == idx )
			{
				return FromString( ascii );
			}
			else
			{
				using ( var sr = new StringReader( ascii.Substring( idx ) ) )
				{
					var split = sr.ReadLine( ).Split( new char[ ] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries );

					return FromString( Encoding.GetEncoding( split[ 1 ].Trim( ) ).GetString( message ) );
				}
			}
		}

		/// <summary>
		/// メッセージを取得します
		/// </summary>
		/// <returns>メッセージ</returns>
		public byte[ ] GetBytes( )
		{
			return Encoding.GetBytes( ToString( ) );
		}

		/// <summary>
		/// メッセージをリクエストまたはレスポンスで取得します
		/// </summary>
		/// <param name="message">メッセージ</param>
		/// <returns>リクエストまたはレスポンス</returns>
		public static Message FromString( string message )
		{
			using ( var sr = new StringReader( message ) )
			{
				var split = sr.ReadLine( ).Split( new char[ ] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries );
				var dummy = 0;

				if ( int.TryParse( split[ 1 ], out dummy ) )
				{
					return new Response( message );
				}
				else
				{
					return new Request( message );
				}
			}
		}
	}

	/// <summary>
	/// メッセージを定義します
	/// </summary>
	public interface IMessage : IParseable
	{
		/// <summary>
		/// ヘッダ群を取得します
		/// </summary>
		HeaderList HeaderList
		{
			get;
		}

		/// <summary>
		/// プロトコルを取得します
		/// </summary>
		Protocol Protocol
		{
			get;
		}

		/// <summary>
		/// メッセージの文字エンコーディングを取得します
		/// </summary>
		Encoding Encoding
		{
			get;
		}

		/// <summary>
		/// メッセージを取得します
		/// </summary>
		/// <returns>メッセージ</returns>
		byte[ ] GetBytes( );
	}
}
